#ifndef CARD_H
#define CARD_H

class Card
{
	public:
		Card();
		Card(Card const &toCopy);
		Card(uint8 Encoded);
		Card(int32 Suit, int32 Rank);
		Card(int32 index);

		int32 getSuit() const;
		int32 getRank() const;
		
		uint8 getSuitChar() const;
		uint8 getRankChar() const;

		bool8 equals(Card* other);
		bool8 operator==(const Card& c) const;

		int32 toIndex();
		int32 toSuit(int32 index);
		int32 toRank(int32 index);

		void fromIndex(int32 index);
		void set(int32 Suit, int32 Rank);

		bool8 isSpecial(int32 Rank);

		uint32 deSerialize(uint8* pBuffer);

		enum Ranking
		{
			ACE		= 1,
			//
			// 2 thru 10 face value
			//
			JACK	= 11,
			QUEEN	= 12,
			KING	= 13
		};

		enum Suits
		{
			CLUBS		= 0x10,
			DIAMONDS	= 0x20,
			HEARTS		= 0x30,
			SPADES		= 0x40,
			SPECIAL		= 0x50
		};

		enum SpecialRanks	// with SPECIAL suit
		{
			JOKER	= 1
		};

	protected:
		uint8 value;
};

inline int32
Card::getSuit() const
{
	return (int32)value & 0xF0;
}

inline int32
Card::getRank() const
{
	return (int32)value & 0x0F;
}

inline uint8
Card::getSuitChar() const
{
	int32 nSuit = getSuit();
	switch( nSuit )
	{
		case CLUBS:
			return 'C';
			break;
		case DIAMONDS:
			return 'D';
			break;
		case HEARTS:
			return 'H';
			break;
		case SPADES:
			return 'S';
			break;
	}

	return 0;
}

inline uint8
Card::getRankChar() const
{
	int32 nRank = getRank();

	if ( (nRank == ACE) || (nRank == 14) )
	{
		return 'A';
	}
	else if ( nRank == JACK )
	{
		return 'J';
	}
	else if ( nRank == QUEEN )
	{
		return 'Q';
	}
	else if ( nRank == KING )
	{
		return 'K';
	}
	else if ( nRank == JOKER )
	{
		return 'J';
	}
	else if ( nRank == 10 )
	{
		return 'T';
	}
	else if ( (nRank > 1) && (nRank < 10) )
	{
		return '0' + (uint8) nRank;
	}
	else 
	{
		// We recieved a rank value not covered above!
		ASSERT( false );
	}

	return 0;
}

inline
int32 Card::toSuit(int32 index)
{
	return ((index / 13) + 1) << 4;
}

inline
int32 Card::toRank(int32 index)
{
	return (index % 13) + 1;
}

inline void
Card::fromIndex(int32 index)
{
	value = (uint8)((toSuit(index) & 0xF0) | (toRank(index) & 0x0F));
}

inline void
Card::set(int32 Suit, int32 Rank)
{
	value = (uint8)((Suit & 0xF0) | (Rank & 0x0F));
}

inline
Card::Card()
{
	value = 0;
}

inline
Card::Card(uint8 Encoded)
{
	value = Encoded;
}

inline
Card::Card(int32 Suit, int32 Rank)
{
	set(Suit, Rank);
}

inline
Card::Card(Card const &toCopy)
{
	value = toCopy.value;
}

inline
Card::Card(int32 index)
{
	fromIndex(index);
}

inline bool8
Card::equals(Card* other)
{
	return value == other->value;
}

inline bool8 
Card::operator==(const Card& c) const
{
	return value == c.value;
}

inline int32
Card::toIndex()
{
	return ((((value - 0x10) & 0xF0) >> 4) * 13) + ((value & 0x0F) - 1);
}

inline bool8
Card::isSpecial(int32 Rank)
{
	return (bool8)(getSuit() == SPECIAL && getRank() == Rank);
}

inline uint32
Card::deSerialize(uint8* pBuffer)
{
	int32 suit, rank;

	switch (pBuffer[0])
	{
		case 'C': suit = Card::CLUBS;		break;
		case 'D': suit = Card::DIAMONDS; 	break;
		case 'H': suit = Card::HEARTS;		break;
		case 'S': suit = Card::SPADES;		break;
		default:  suit = 0;	                break;
	}
	switch (pBuffer[1])
	{
		case 'A': rank = Card::ACE;			break;
		case 'T': rank = 10;				break;
		case 'J': rank = Card::JACK;		break;
		case 'Q': rank = Card::QUEEN;		break;
		case 'K': rank = Card::KING;		break;
		default:  rank = pBuffer[1] - '0';	break;
	}

	set(suit, rank);

    return 2;
}

#endif
